using System;
using System.Collections.Generic;
using System.Text;
using Org.BouncyCastle.Bcpg.OpenPgp;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Bcpg;
using Pbdms.Common.SharedVariables;
using System.Collections;
namespace Pbdms.Common.CryptoUtils
{
    public class Truster
    {

        public const int UNDISCOVERED = 1;
        public const int DISCOVERED = 2;
        public const int COMPLETED_EXPLORED = 3;
        public Queue<string> answer;
        private PublicKeysControl publicKeyControl;
        public Truster()
        {
            publicKeyControl = null;
        }
        public Truster(PublicKeysControl publicKeyControl)
        {
            this.publicKeyControl = publicKeyControl;
        }
        public Boolean checkTrust(PgpPublicKey KeyToBeVerified,PgpSecretKey secKey,ArrayList buddyList, ref Queue<string> path)
        {
            //web of trust

            if (KeyToBeVerified == null)
            {
                return false;
            }
            ArrayList KeyData = new ArrayList();
            //add sec key
            KeyData.Add(secKey.KeyId.ToString("X"));
            ArrayList temp = buddyList;
            ArrayList arlPublicKeys = new ArrayList();
            foreach(Common.DataObject.Buddy buddy in temp)
            {
                if (buddy.KeyID.Equals(secKey.KeyId.ToString("X")))
                {
                }
                else
                {
                    //add public key
                    arlPublicKeys.Add(buddy.KeyID);
                    KeyData.Add(buddy.KeyID);
                }
            }
            int start = -1;
            //initialize 
            int[] parent = new int[arlPublicKeys.Count + 1];
            int[] state = new int[arlPublicKeys.Count + 1];
            for (int count = 0; count < arlPublicKeys.Count + 1; count++)
            {
                parent[count] = -1;
                state[count] = UNDISCOVERED;
            }
            Queue<PgpPublicKey> que= new Queue<PgpPublicKey>();
            //queue the key to be checked
            que.Enqueue(KeyToBeVerified);
            Verifier verifier = new Verifier();
            Boolean found = false;
            ///based on breadth first search
            while(que.Count!=0 && found==false)
            { 
                //pop the key to be check
                PgpPublicKey currentNode= que.Dequeue();
                //loop all signatures               
                foreach (PgpSignature sign in currentNode.GetSignaturesOfType(PgpSignature.DirectKey))
                {
                    //if the signature equal to secret key 
                    if (secKey.KeyId.Equals(sign.KeyId))
                    {
                        //verify the signature
                        if (verifier.VerifySignature(sign, secKey.PublicKey,  currentNode.KeyId.ToString("X")))
                        {
                            if (state[KeyData.IndexOf(sign.KeyId.ToString("X"))] == UNDISCOVERED)
                            {
                                //indicate the parent of the current signature
                                parent[KeyData.IndexOf(sign.KeyId.ToString("X"))] = KeyData.IndexOf(currentNode.KeyId.ToString("X"));
                                state[KeyData.IndexOf(sign.KeyId.ToString("X"))] = DISCOVERED;
                                start = KeyData.IndexOf(sign.KeyId.ToString("X"));
                                //break the loop
                                found = true;
                            }
                        }
                    }
                    else 
                    {

                        PgpPublicKey pubkeyToBeCheck = publicKeyControl.getPublicKey(sign.KeyId.ToString("X"));
                        if (pubkeyToBeCheck != null)
                        {        
                            if (verifier.VerifySignature(sign, pubkeyToBeCheck, currentNode.KeyId.ToString("X")))
                            {
                                if (state[KeyData.IndexOf(sign.KeyId.ToString("X"))] == UNDISCOVERED)
                                {
                                    //queue up the signature's public key
                                    state[KeyData.IndexOf(sign.KeyId.ToString("X"))] = DISCOVERED;
                                    que.Enqueue(publicKeyControl.getPublicKey(sign.KeyId.ToString("X")));
                                    parent[KeyData.IndexOf(sign.KeyId.ToString("X"))] = KeyData.IndexOf(currentNode.KeyId.ToString("X"));
                                }
                            }
                        }
                    }
                    
                }
                state[KeyData.IndexOf(currentNode.KeyId.ToString("X"))] = COMPLETED_EXPLORED;
            }
            
            if (found==true)
            {
                path=Arrange(parent, KeyData, start, KeyToBeVerified.KeyId);
            }

            return found;
    
        }
        public Queue<string> Arrange(int[] parent, ArrayList keyData, int start, long starting_node)
        {

            Array temp= keyData.ToArray();
            Queue<string> que= new Queue<string>();

            while (parent[start]!=-1)
            {
                string tmpKeyID=(string)temp.GetValue(start);
                que.Enqueue(tmpKeyID);
                start=parent[start];
                //to prevent a self loop
                if (start == parent[start])
                {
                    break;
                }
            }
            que.Enqueue(starting_node.ToString("X"));
            return que;
        }

    }
    
    
}
